﻿using CsvHelper;
using Share;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;

/// <summary>
/// 资源数据基类
/// </summary>
public abstract class ResDataBase
{
    /// <summary>
    /// 资源ID
    /// </summary>
    public long Id { get; private set; } = -1;

    /// <summary>
    /// 是否自动更新流
    /// </summary>
    public bool AutoUpStream { get; set; } = false;

    protected ResDataBase()
    {
    }

    /// <summary>
    /// 获取主ID
    /// </summary>
    public long GetMainID() => Id;

    private string _tableName;
    private bool _showError = true;

    /// <summary>
    /// 设置是否显示错误
    /// </summary>
    protected void ShowError(bool value) => _showError = value;
    protected string GetString(string key, bool isShowError = true)
    {
        return GetValue<string>(key, "");

    }

    protected bool GetBool(string key)
    {
        return GetValue<bool>(key, true);
    }
    protected long GetLong(string key)
    {
        return GetValue<long>(key, 0);
    }
    protected int GetInt(string key)
    {
        return int.Parse(GetValue(key, "-1"));
    }

    protected List<int> GetIntList(string key)
    {
        return StringTools.GetList<int>(GetValue(key, ""));
    }
    protected List<long> GetLongList(string key)
    {
        return StringTools.GetList<long>(GetValue(key, ""));
    }
    protected List<float> GetFloatList(string key)
    {
        return StringTools.GetList<float>(GetValue(key, ""));
    }
    protected List<double> GetDoubleList(string key)
    {
        return StringTools.GetList<double>(GetValue(key, ""));
    }
    protected List<string> GetStringList(string key)
    {
        return StringTools.GetStringList(GetValue(key, ""));
    }

    /// <summary>
    /// 获取指定键的值
    /// </summary>
    public T GetValue<T>(string key, T defaultValue)
    {
        if (!_head.TryGetValue(key, out var index))
            return defaultValue;

        if (index >= _values.Count)
            return defaultValue;

        if (_values[index].Equals(""))
            return defaultValue;

        try
        {
            return (T)Convert.ChangeType(_values[index], typeof(T));
        }
        catch (Exception ex)
        {
            XKLog.LogError($"Failed to convert value for key {key}: {ex.Message}");
            return defaultValue;
        }
    }

    protected double GetDouble(string key)
    {
        return GetValue<double>(key, 0);
    }
    protected float GetFloat(string key)
    {
        return GetValue<float>(key, 0);
    }


    protected virtual void OnInit()
    {
        Id = GetLong("id");
    }
    // 如果有需要对表格数据进行二次处理的， 重载这个函数，
    protected virtual void OnInitCustom()
    {

    }
    private List<string> _values;
    private Dictionary<string, int> _head;

    /// <summary>
    /// 初始化数据
    /// </summary>
    public void DoInit(List<string> value, Dictionary<string, int> head, string tableName)
    {
        _values = value ?? throw new ArgumentNullException(nameof(value));
        _head = head ?? throw new ArgumentNullException(nameof(head));
        _tableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
        OnInit();
        OnInitCustom();
    }
}

public class ResDataMgr<T> where T : ResDataBase, new()
{
    #region
    private static ResDataMgr<T> m_instance;
    public static ResDataMgr<T> Instance()
    {
        if (m_instance == null)
        {
            lock (locker)
            {
                m_instance = new ResDataMgr<T>();
            }
        }
        return m_instance;
    }
    #endregion

    bool m_bInited = false;
    bool m_bAutoUpStream = false;
    private static readonly object locker = new object();
    Dictionary<long, T> m_DicData = new Dictionary<long, T>();
    List<T> m_ListData = new List<T>();
    string m_strTableName;

    // 维护一个缓存的数据，如果连续访问一个id，可以速度快点
    T m_ResData = null;

    public void DoLinkTxt(string strName, bool autoUpStream = false)
    {
        m_strTableName = strName;
        m_bAutoUpStream = autoUpStream;
    }
    public Dictionary<long, T> GetTableData()
    {
        return m_DicData;
    }
    string GetFileFullName(string name)
    {
        return string.Format("Table/{0}", name);
    }
    public void DoInit(string strTableName, bool bAutoUpStream = false, bool bReload = false)
    {
        if (m_bInited)
            return;
        m_bAutoUpStream = bAutoUpStream;
        long nCurID = -1;
        try
        {
            m_DicData.Clear();
            m_ListData.Clear();
            m_strTableName = strTableName;
            TextAsset obj = Resources.Load<TextAsset>(GetFileFullName(strTableName));
            using (var steam = new MemoryStream(Encoding.UTF8.GetBytes(obj.text)))
            {
                using (var reader = new StreamReader(steam))
                {
                    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
                    {
                        csv.Read();
                        csv.ReadHeader();
                        Dictionary<string, int> keyValuePairs = new Dictionary<string, int>();
                        for (int i = 0; i < csv.HeaderRecord.Length; ++i)
                        {
                            if (csv.HeaderRecord[i] != "")
                                keyValuePairs.Add(csv.HeaderRecord[i], i);
                        }
                        while (csv.Read())
                        {
                            T core = new T();
                            if (core != null)
                            {
                                var tableValue = csv.Parser.Record.ToList();
                                core.AutoUpStream = bAutoUpStream;
                                core.DoInit(tableValue, keyValuePairs, strTableName);
                                m_DicData.Add(core.GetMainID(), core);
                                m_ListData.Add(core);
                            }
                        }
                        m_ListData.Sort(delegate (T a, T b)
                        {
                            if (a.Id > b.Id)
                                return 1;
                            return -1;
                        });
                    }
                }

            }


            m_bInited = true;
        }
        catch (System.ArgumentOutOfRangeException e)
        {
            XKLog.LogError("{2}表格[{0}]里的[{1}]解析了错误的字符串", m_strTableName, nCurID, e.Message);
        }
        catch (System.ArgumentException e)
        {
            XKLog.LogError("{2}表格[{0}]发现重复的id [{1}]", m_strTableName, nCurID, e.Message);
        }
        catch (System.Exception e)
        {
            XKLog.LogError("{1}表格[{0}]读取错误!", m_strTableName, e.Message);
            //XKLog.LogError(e.Message);
        }
    }
    public void DoReload()
    {
        m_bInited = false;
        m_ResData = null;
        DoInit(m_strTableName, m_bAutoUpStream);
    }

    public void DoInit()
    {
        if (!m_bInited)
        {
            if (string.IsNullOrEmpty(m_strTableName))
            {
                XKLog.LogError("初始化没有link的表格，{0}", ToString());
                return;
            }
            DoInit(m_strTableName, m_bAutoUpStream);
        }
    }
    public void DoAutoLoad(string strTableName, bool bAutoUpStream = false, bool bReset = false)
    {
        m_bInited = false;
        m_ResData = null;
        DoInit(strTableName, bAutoUpStream, bReset);
    }
    public bool IsHaveID(long nID)
    {
        if (!m_bInited)
        {
            DoInit(m_strTableName, m_bAutoUpStream);
        }
        if (m_DicData.ContainsKey(nID))
        {
            return true;
        }
        if (m_bAutoUpStream)
        {


        }
        return false;
    }

    public T GetResDataByID(long nID)
    {
        if (nID < 0)
            return null;
        if (!m_bInited)
        {
            DoInit(m_strTableName, m_bAutoUpStream);
            //XKLog.LogError("{0}没有就初始化就用了", ToString() );
        }
        if (m_ResData != null && m_ResData.GetMainID() == nID)
        {
            return m_ResData;
        }
        if (m_DicData.ContainsKey(nID))
        {
            m_ResData = m_DicData[nID];
            return m_ResData;
        }

        if (m_bAutoUpStream)
        {
            // 如果没有找到，就开始往前找最近的组

            XKLog.LogError("[{0}]表里没有[{1}]的id,而且也没有这个id的基础id", m_strTableName, nID);
        }
        else
            XKLog.LogError("[{0}]表里没有[{1}]的id", m_strTableName, nID);
        return null;
    }
    public List<T> GetResDataListByMainID(long nMainID)
    {
        List<T> list = new List<T>();
        if (!m_bInited)
        {
            DoInit(m_strTableName, m_bAutoUpStream);
        }
        return list;
    }

    public string GetTableName()
    {
        return m_strTableName;
    }
    public List<T> GetResData()
    {
        if (!m_bInited)
        {
            DoInit(m_strTableName, m_bAutoUpStream);
            //XKLog.LogError("{0}没有就初始化就用了", ToString() );
        }
        return m_ListData;
    }
    public int GetResDataNumber()
    {
        if (!m_bInited)
        {
            DoInit(m_strTableName, m_bAutoUpStream);
            //XKLog.LogError("{0}没有就初始化就用了", ToString() );
        }
        return m_ListData.Count;
    }
    public T GetResDataByIndex(int nIndex)
    {
        if (!m_bInited)
        {
            DoInit(m_strTableName, m_bAutoUpStream);
            //XKLog.LogError("{0}没有就初始化就用了", ToString() );
        }
        if (nIndex >= GetResDataNumber())
            return null;
        return m_ListData[nIndex];
    }

    public void GetIDList(List<int> list)
    {
        if (!m_bInited)
        {
            DoInit(m_strTableName, m_bAutoUpStream);
            //XKLog.LogError("{0}没有就初始化就用了", ToString() );
        }
        foreach (var data in m_ListData)
        {
            list.Add((int)data.GetMainID());
        }
    }


    public T GetLastData()
    {
        return m_ListData.Last();
    }
}
